/**
 @file Reply_Handler.cpp

 Handles replying messages reacted by multi-cast messages in MDNS

 @author Kyoungho An <kyoungho.an@gmail.com>
*/

#ifndef _REPLY_HANDLER_CPP_
#define _REPLY_HANDLER_CPP_

#include "Reply_Handler.h"

using namespace MDNS;

Reply_Handler::Reply_Handler (ACE_Reactor *r)
: ACE_Event_Handler (r)
{
	ACE_DEBUG ((LM_DEBUG, "Reply_Handler constructor\n"));
}

int Reply_Handler::open (struct mdnsd *svr)
{
	ACE_DEBUG ((LM_DEBUG, "Reply_Handler::init \n"));

	// initialize MDNS daemon object
	this->svr_ = svr;

	// open multicast socket
	this->mcast_addr_.set (MDNS_PORT, MDNS_ADDR);
	if (this->mcast_dgram_.join (mcast_addr_) == -1)
	{
		ACE_ERROR ((LM_ERROR, "%p\n", "join"));
		return -1;
	}

	// allocate memory for replying packet
	this->mdns_reply_ = (struct mdns_pkt *)malloc(sizeof(struct mdns_pkt));
	memset (this->mdns_reply_, 0, sizeof(struct mdns_pkt));

	// register the handler to the reactor
	if (this->reactor ()->register_handler (this,
		ACE_Event_Handler::READ_MASK) == -1)
	{
		ACE_ERROR ((LM_ERROR, "Failed to register handler\n"));
		return -1;
	}

	return 0;
}

int Reply_Handler::handle_input (ACE_HANDLE)
{
	ACE_DEBUG ((LM_DEBUG, "Reply_Handler::handle_input\n"));

	// receive a packet
	ssize_t recv_size = this->mcast_dgram_.recv(this->pkt_buf_, BUFFER_SIZE,
		mcast_addr_);

	if (recv_size < 0)
		ACE_ERROR ((LM_ERROR, "Receiving a packet failed\n"));

	ACE_DEBUG ((LM_DEBUG, "Received Packet Size: %d\n", recv_size));
	ACE_DEBUG ((LM_DEBUG, "Received Packet from: %s\n",
		mcast_addr_.get_host_addr ()));

	// parse a packet
	struct mdns_pkt *mdns = mdns_parse_pkt (this->pkt_buf_, recv_size);
	if (mdns != NULL) {
		ACE_DEBUG ((LM_DEBUG, "Reply_Handler::MDNS is not null!\n"));
		// process the received packet
		if (process_mdns_pkt (this->svr_, mdns, this->mdns_reply_)) {
			// encode and send replying packet
			size_t replylen = mdns_encode_pkt (this->mdns_reply_,
				this->pkt_buf_, BUFFER_SIZE);
			mcast_dgram_.send (this->pkt_buf_, replylen);
			ACE_DEBUG ((LM_DEBUG, "Reply_Handler::Replied packet length: %d\n",
				replylen));
		}
		mdns_pkt_destroy (mdns);
	}

	return 0;
}

// handle clean up events
int Reply_Handler::handle_close (ACE_HANDLE)
{
	ACE_DEBUG ((LM_DEBUG, "Reply_Handler::handle_close\n"));
	ACE_ASSERT(this->svr_ != NULL);

	// main thread terminating. send out "goodbye packets" for services
	mdns_init_reply(this->mdns_reply_, 0);

	struct rr_list *svc_le = this->svr_->services;
	for (; svc_le; svc_le = svc_le->next) {
		// set TTL to zero
		svc_le->e->ttl = 0;
		this->mdns_reply_->num_ans_rr +=
			rr_list_append(&(this->mdns_reply_->rr_ans), svc_le->e);
	}

	// send out packet
	if (this->mdns_reply_->num_ans_rr > 0) {
		size_t replylen = mdns_encode_pkt(this->mdns_reply_, this->pkt_buf_, BUFFER_SIZE);
		this->mcast_dgram_.send (this->pkt_buf_, replylen);		
	}

	// destroy packet
	mdns_init_reply(this->mdns_reply_, 0);

	if (this->mdns_reply_)
		free(this->mdns_reply_);

	this->mcast_dgram_.leave (this->mcast_addr_);

	rr_group_destroy(this->svr_->group);
	rr_list_destroy(this->svr_->announce, 0);
	rr_list_destroy(this->svr_->services, 0);

	if (this->svr_->hostname)
		free(this->svr_->hostname);
	return 0;
}

#endif
